﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace TaskDemo
{
    public class Demo
    {
        // Task如何启动线程
        public void Demo1()
        {
            // 第一种方式
            Task task = new Task(() =>
            {
                Console.WriteLine($"开启了一个线程：ThreadId:{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
            });
            task.Start();

            // 第二种方式
            Task.Run(() => Console.WriteLine($"开启了一个线程：ThreadId:{Thread.CurrentThread.ManagedThreadId.ToString("00")}"));

            // 第三种方式
            TaskFactory factory = new TaskFactory();
            factory.StartNew(() => Console.WriteLine($"开启了一个线程：ThreadId:{Thread.CurrentThread.ManagedThreadId.ToString("00")}"));

            Thread.Sleep(2000); // 等待---阻塞界面---因为阻塞了主线程--- 主线程等待
            Task.Delay(1000); // Delay：延迟，不会阻塞主线程，主线程中执行到之力，会继续往后，等待多长时间之后，再去执行一个行为--回调
        }

        public void Demo2()
        {
            // 关于多线程，什么情况下可以使用？
            //1.可以并发执行

            //Task.WaitAny(taskList.ToArray()); // 等待这有一堆任务其中有一个执行结束，就继续往后执行，等待也是等待主线程---阻塞界面
            //Task.WaitAll(taskList.ToArray()); // 得带所有的任务执行完成，等待也是等待主线程--阻塞界面的

            
        }

        public void TaskExceptionDemo1()
        {
            // 一、多线程内部发生异常侯，直接try-catch包裹，是无法捕捉到异常的---当然无法处理的
            // 1、异常一定是发生了
            // 2、异常一定是被吞掉了
            // 二、多线程内部发生的异常，如何才能捕捉到呢？
            // 1、需要做线程的等待 调用Task.WaitAll
            // 2、也需要try-catch包裹
            // 3、可以捕捉到异常了
            // 4、捕捉到的异常是一个AggregateException 类型的异常
            // 5、try-catch 一个 try对应多个catch
            // 6、try内部发生异常后，在匹配这个catch中的异常类型的时候，优先具体类型的匹配，在抽象匹配；
            // 7、AggregateException 内部包含的有多线程内部异常的集合

            #region  多线程异常处理
            for (int i = 0; i < 10; i++)
            {
                List<Task> tasklist = new List<Task>();
                try
                {
                    string k = $"AsyncCorebtn_click_{i}";
                    tasklist.Add(Task.Run(() =>
                    {
                        if (k.Equals("AsyncCorebtn_click_2"))
                        {
                            Console.WriteLine("AsyncCorebtn_click_2");
                            throw new Exception("AsyncCorebtn_click_2抛出了异常");
                        }
                        else if (k.Equals("AsyncCorebtn_click_4"))
                        {
                            Console.WriteLine("AsyncCorebtn_click_4");
                            throw new Exception("AsyncCorebtn_click_4抛出了异常");
                        }
                        else if (k.Equals("AsyncCorebtn_click_6"))
                        {
                            Console.WriteLine("AsyncCorebtn_click_6");
                            throw new Exception("AsyncCorebtn_click_6抛出了异常");
                        }
                    }));
                    Task.WaitAll(tasklist.ToArray());
                }
                catch (AggregateException e) // AgregateException 是Exception的子类，也是多线程的专属异常类型
                {
                    foreach (var exception in e.InnerExceptions)
                    {
                        Console.WriteLine(exception.Message);
                    }
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                }
            }
            #endregion

        }

        public void TaskExceptionDemo2()
        {
            /**
             * 三、线程的取消
             * 1.线程是不能从外部取消--只能自己取消自己--只有上帝才能制造出一块上帝搬不动的石头；
             * 2.可以来一个信号量[通知],多个线程同时执行的时候，定义一个信号量，如果有线程异常了，
             *      就去通知信号量，另外的线程在执行的时候，时常去查看下信号量，如果信号量被改变了，直接取消自己；
             * 3.如何实现？ 可以定义一个bool值做信号量bool isGoOn = true -- 不被推荐，不是线程安全的。
             * 4、标准实现--cancellationTokenSource,提供了ISCancellationRequested 属性-- 默认为false，默认不取消，
             *      同时也提供了一个Cancel()方法，cancel()方法就是把IsCancellationRequested 把false变成true；变成
             *      true之后，就标识需要取消了
             * 
             * 线程启动---C#发起命令---向操作系统去申请线程资源，申请也需要消耗时间的；---延迟启动
             * 案例：一共要启动10个线程---因为是延迟启动---有一个线程启动成功了，同时还有刷那个线程已启动成功了。
             *      但是第一个线程如果发生了异常了；已经启动的另外三个线程，要不就是在执行的初期异常了，被吞掉了（取消了）
             *      还有6个线程准备启动，但是还没有启动起来:这6个线程如果启动后，最终的结局：必然也要异常取消掉；既然如此
             *      启动后也是白启动，就应该让还没有启动的这6个线程不要启动了；
             * */
            #region 线程取消

            CancellationTokenSource cts = new CancellationTokenSource();

            for (int i = 0; i < 10; i++)
            {
                Task.Run(() =>
                {
                    if (cts.IsCancellationRequested == false)
                    {
                        Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId.ToString("00")}：线程正常开始！");
                    }
                    else
                    {
                        Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId.ToString("00")}:线程无法正常开始！");
                        throw new Exception("线程取消了");
                    }
                    try
                    {
                        string k = $"AsyncCorebtn_click_{i}";
                        if (k.Equals("AsyncCorebtn_click_2"))
                        {
                            Console.WriteLine("AsyncCorebtn_click_2");
                            throw new Exception("AsyncCorebtn_click_2抛出了异常");
                        }
                        else if (k.Equals("AsyncCorebtn_click_4"))
                        {
                            Console.WriteLine("AsyncCorebtn_click_4");
                            throw new Exception("AsyncCorebtn_click_4抛出了异常");
                        }
                        else if (k.Equals("AsyncCorebtn_click_6"))
                        {
                            Console.WriteLine("AsyncCorebtn_click_6");
                            throw new Exception("AsyncCorebtn_click_6抛出了异常");
                        }
                    }
                    catch (Exception e) {
                        cts.Cancel();
                    }

                    if (cts.IsCancellationRequested == false)
                    {
                        Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId.ToString("00")}：线程正常结束！");
                    }
                    else
                    {
                        Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId.ToString("00")}:线程无法正常结束！");
                        throw new Exception("线程取消了");
                    }

                },cts.Token);
            }
            #endregion

            #region 临时变量
            for (int i = 0; i < 20; i++)
            {
                Task.Run(() =>
                {
                    Thread.Sleep(1000);
                    Console.WriteLine($"i is{i}");
                });
            }

            // 一、以上代码的执行，输出的i的结果：会包含哪些数据? 答案是只有20
            // 二、为什么这个i直接等于20了？
            //1.多线程启动 是延迟启动[时间肯定很短]，同时多线程启动不会阻塞主线程
            #endregion
        }
    }
}
